function cloneDeep(source) {
  const type = Object.prototype.toString.call(source);
  console.log("type", type);
  switch (type) {
    case "[object String]":
    case "[object Number]":
    case "[object Null]":
    case "[object Undefined]":
      return source;
    case "[object Object]":
      const target = {};
      Object.keys(source).reduce((pre, curr) => {
        target[curr] = source[curr];
      }, target);
      return target;
    case "[object Array]":
      return source.map((i) => cloneDeep(i));
    case "[object Date]":
      return new Date(source.getTime());
    case "[object Regex]":
      return new RegExp(source);
    case "[object Function]":
      return new Function(`return ${source.toString()}`)();
  }
}

var r = cloneDeep({ a: "a", b: [1, 2, 3], fn: cloneDeep });

console.log(r);
